﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Configuration;
using Taobao.GatherInform.Model;
using Taobao.Framework;
using System.Drawing;
using System.ComponentModel;
using System.Globalization;

namespace Taobao.GatherInform
{
    public class GatherInformationProcessor:Taobao.Framework.IProcessor
    {
        public const string UpdateStatus_None = "none";
        public const string UpdateStatus_Waiting ="waiting";
        public const string UpdateStatus_Processing = "processing";
        public const string UpdateStatus_End = "end";

        #region Ctor

        public GatherInformationProcessor(TextWriterBase textWriter)
        {
            PageSize = 50;
            TextWriter = textWriter;
            Initialize();
        }

        #endregion

        #region Properties

        protected Thread Job
        {
            get;
            set;
        }

        protected Top.Api.ITopClient TopClient
        {
            get;
            set;
        }

        protected string ServiceUrl
        {
            get;
            set;
        }
         

        protected Shop Shop
        {
            get;
            set;
        }

        public int PageSize
        {
            get;
            set;
        }

        public Query Query
        {
            get;
            set;
        }

        protected DateTime UpdateStartDateTime
        {
            get;
            set;
        }
        #endregion

        #region Events

        public event EventHandler<EventArgs> ProcessCompleted;

        #endregion

        #region IProcessor Members

        public Framework.IProcessorConfig Config
        {
            get;
            set;
        }

        public void Start()
        {
            Job = new Thread(new ThreadStart(DoProcess));
            Job.Start();
        }

        public void End()
        {
            try
            {
                if (Job != null)
                {
                    Job.Abort();
                }

            }
            catch (Exception ex)
            { 
            }
        }

        public TextWriterBase TextWriter
        {
            get;
            set;
        }

#endregion

        protected void Initialize()
        {
            ServiceUrl = ConfigurationManager.AppSettings["ServiceUrl"];
            using (var ObjectContext = new Model.taobaogatherEntities()) 
            {
                Shop = ObjectContext.Shops.SingleOrDefault();
                if (Shop == null) {
                    Console.WriteLine("数据库里面没有Shop 信息，初始化失败！");
                    return;
                }
            }

            TopClient = new Top.Api.DefaultTopClient(ServiceUrl, Shop.AppKey, Shop.AppSecret);
        }

        protected void DoProcess()
        {
            bool needGather = false;
            using (var context = new Model.taobaogatherEntities())
            {
                try
                {
                    var shop = context.Shops.Where(s => s.Id == Shop.Id).SingleOrDefault();
                    if (ConfigurationManager.AppSettings[GatherForm.DBStatusControKey] == "true")
                    {
                        needGather = shop.Status == GatherInformationProcessor.UpdateStatus_Waiting;
                    }
                    else
                    {
                        needGather = true;
                    }
                }
                catch { }
            }
            if (needGather)
            {
                UpdateShopStatus(UpdateStatus_Processing);
                GatherInfo();
                UpdateShopStatus(UpdateStatus_End);
                if (ProcessCompleted != null)
                {
                    ProcessCompleted.Invoke(this, EventArgs.Empty);
                }
            }
        }

        protected void GatherInfo()
        {
            UpdateStartDateTime = DateTime.Now;
            long total = 0;
            long success = 0; 
            try
            {
                TextWriter.Clear();
                OutputInfo("抓取数据开始");

                Top.Api.Request.ItemsSearchRequest itemsSearchRequest = new Top.Api.Request.ItemsSearchRequest();
                itemsSearchRequest.PageSize = PageSize;
                itemsSearchRequest.Fields = "num_iid,iid,title,nick,pic_url,cid,price,type,delist_time,post_fee,score,volume,has_discount, num, is_prepay, promoted_service, ww_status, list_time  ";
                itemsSearchRequest.Q = Query.Title;
                itemsSearchRequest.AuctionFlag = Query.AuctionFlag;
                itemsSearchRequest.EndPrice = Query.EndPrice;
                itemsSearchRequest.StartPrice = Query.StartPrice;
                itemsSearchRequest.WwStatus = Query.WWStatus;
                itemsSearchRequest.StuffStatus = Query.StuffStatus;
                itemsSearchRequest.PromotedService = Query.PromotedService;
                itemsSearchRequest.Nicks = Query.Nicks;
                itemsSearchRequest.LocationCity = Query.City;
                itemsSearchRequest.LocationState = Query.State;
                itemsSearchRequest.HasDiscount = Query.HasDiscount;

                Top.Api.Response.ItemsSearchResponse itemsSearchResponse = TopClient.Execute(itemsSearchRequest);
                if (itemsSearchResponse.ItemSearch != null && itemsSearchResponse.ItemSearch.Items != null)
                {
                    try
                    {
                        success = ProcessPageList<Top.Api.Domain.Item>("抓取数据", PageSize, itemsSearchResponse.ItemSearch.Items,
                           (pageNo) =>
                           {
                               itemsSearchRequest.PageNo = pageNo;
                               var response = TopClient.Execute(itemsSearchRequest);
                               if (response != null && response.ItemSearch != null
                                    && response.ItemSearch.Items != null)
                               {
                                   return response.ItemSearch.Items;
                               }
                               return new List<Top.Api.Domain.Item>();
                           },
                           UpdateItem, itemsSearchResponse.TotalResults);
                    }
                    catch { }

                }
                else
                {
                    Console.WriteLine("对不起，没有找到符合您要求的数据。");
                }
                OutputInfo("抓取数据结束");
            }
            catch (Exception ex)
            {
                OutputInfo(string.Format("抓取数据异常：{0}", ex.Message));
            } 
        }

        protected bool UpdateItem(Top.Api.Domain.Item item)
        {
            bool result = false;
            try
            {
                OutputInfo(string.Format("更新商品 {0}的信息开始", item.NumIid));
                result = SaveItemToDb(item, false);
                UpdateUpdatedShops(item);
                result &= UpdateItemDetails(item);
                OutputInfo(string.Format("更新商品 {0}的信息结束", item.NumIid));
            }
            catch { }
            return result;
        }

        public bool UpdateShopStatus(string status)
        {
            bool result = false;
            try
            {
                if (Shop != null)
                {
                    using (var context = new Model.taobaogatherEntities())
                    {
                        var shop = context.Shops.Where(s => s.Id == Shop.Id).SingleOrDefault();
                        if (shop != null)
                        {
                            shop.Status = status;
                            context.SaveChanges();
                            result = true;
                        }
                    }
                }
            }
            catch
            { 
            }
            return result;
        }

        protected bool UpdateUpdatedShops(Top.Api.Domain.Item item)
        {
            bool result = false;
            try
            {
                using (var context = new Model.taobaogatherEntities())
                {
                    var updateWeekIndex = GetWeekOfYear(UpdateStartDateTime);
                    var shop = (from s in context.UpdatedShops where s.Nick == item.Nick select s).SingleOrDefault();
                    if (shop != null && shop.LastUpdatedYear == UpdateStartDateTime.Year && shop.LastUpdatedWeekIndex == updateWeekIndex)
                    {
                        return true;
                    }

                    bool needAdd = shop == null;
                    if (needAdd)
                    {
                        shop = new UpdatedShop();
                        shop.Nick = item.Nick;
                    }
                    shop.LastUpdatedWeekIndex = updateWeekIndex;
                    shop.LastUpdatedYear = UpdateStartDateTime.Year;
                    shop.LastUpdatedDate = UpdateStartDateTime;

                    UpdateUpdatedShopDetailInfo(item.Nick, shop );

                    if (needAdd)
                    {
                        context.UpdatedShops.AddObject(shop);
                    }
                    context.SaveChanges();
                    result = true;
                }
            }
            catch {
            }
            return result;
        }

        protected bool UpdateUpdatedShopDetailInfo(string nick, UpdatedShop updatedShop)
        {
            var result = false;
            try
            {
                Top.Api.Request.UserGetRequest userGetRequest = new Top.Api.Request.UserGetRequest();
                userGetRequest.Fields = "uid,nick,buyer_credit.level,buyer_credit.score,buyer_credit.total_num,buyer_credit.good_num,seller_credit.level,seller_credit.score,seller_credit.total_num,seller_credit.good_num";
                userGetRequest.Fields += ",location.zip,location.address,location.city,location.state,location.country,location.district";
                userGetRequest.Nick = nick;
                Top.Api.Response.UserGetResponse userGetResponse = TopClient.Execute<Top.Api.Response.UserGetResponse>(userGetRequest);
                if (!userGetResponse.IsError && userGetResponse.User!=null)
                {
                    var user = userGetResponse.User;
                    updatedShop.UID = user.Uid;
                    if (user.Location != null)
                    {
                        updatedShop.Location_Address = user.Location.Address;
                        updatedShop.Location_City = user.Location.City;
                        updatedShop.Location_Country = user.Location.Country;
                        updatedShop.Location_District = user.Location.District;
                        updatedShop.Location_State = user.Location.State;
                        updatedShop.Location_Zip = user.Location.Zip;
                    }
                    if (user.BuyerCredit != null)
                    {
                        updatedShop.Buyer_Credit_GoodNum = user.BuyerCredit.GoodNum;
                        updatedShop.Buyer_Credit_Level = user.BuyerCredit.Level;
                        updatedShop.Buyer_Credit_Score = user.BuyerCredit.Score;
                        updatedShop.Buyer_Credit_TotalNum = user.BuyerCredit.TotalNum;

                    }
                    if (user.SellerCredit != null)
                    {
                        updatedShop.Seller_Credit_GoodNum = user.SellerCredit.GoodNum;
                        updatedShop.Seller_Credit_Level = user.SellerCredit.Level;
                        updatedShop.Seller_Credit_Score = user.SellerCredit.Score;
                        updatedShop.Seller_Credit_TotalNum = user.SellerCredit.TotalNum;
                    }
                }
                result = true;
            }
            catch { 
            }
            return result;
        }

        protected bool UpdateItemDetails(Top.Api.Domain.Item item)
        {
            bool result = false;
            try
            {
                Top.Api.Request.ItemGetRequest itemGetRequest = new Top.Api.Request.ItemGetRequest();
                itemGetRequest.Fields = "num_iid,iid,detail_url,title,nick,type,props_name,created,promoted_service,cid,seller_cids,props,input_pids,input_str,desc,pic_url,num,valid_thru,list_time,delist_time,stuff_status,price,post_fee,express_fee,ems_fee,has_discount,freight_payer,has_invoice,has_warranty,has_showcase,modified,increment,approve_status,postage_id,product_id,auction_point,property_alias,outer_id,is_virtual,is_taobao,is_ex,is_timing,is_3D,score,volume,one_station,second_kill,auto_fill,violation,is_prepay,ww_status,wap_desc,wap_detail_url,after_sale_id,cod_postage_id,sell_promise,location.zip,location.address,location.city,location.state,location.country,location.district";
                itemGetRequest.NumIid = item.NumIid; 
                Top.Api.Response.ItemGetResponse itemGetResponse = TopClient.Execute(itemGetRequest);
                if (!itemGetResponse.IsError)
                {
                    OutputInfo(string.Format("获得商品 {0} 的详细信息成功",item.NumIid));
                    result = SaveItemToDb(itemGetResponse.Item,true);
                    OutputInfo(string.Format("更新商品 {0} 的详细信息{1}", item.NumIid,
                        result ? "成功" : "失败"));
                }
                else
                {
                    OutputInfo(string.Format("获得商品 {0} 的详细信息失败", item.NumIid));
                    result = false;
                }
            }
            catch (Exception ex)
            {
                result = false;
            }
            return result;
        }

        protected bool SaveItemToDb(Top.Api.Domain.Item item, bool detailInfo)
        {
            var result = false;
            try
            {
                using (var context = new Model.taobaogatherEntities())
                {
                    var updateWeekIndex = GetWeekOfYear(UpdateStartDateTime);
                    var dbItem = (from i in context.Items where i.NumIId == item.NumIid
                                                             && i.UpdateWeekIndex == updateWeekIndex
                                                             && i.UpdateYear == UpdateStartDateTime.Year
                                  select i).FirstOrDefault();
                    var needAdd = false;
                    if (dbItem == null)
                    {
                        needAdd = true;
                        dbItem = new Item();
                    }

                    CopyItem(item, dbItem, detailInfo);
                    
                    //For self
                    dbItem.UpdateDate = UpdateStartDateTime;
                    dbItem.UpdateYear = UpdateStartDateTime.Year;
                    dbItem.UpdateWeekIndex = updateWeekIndex;

                    if (needAdd)
                    {
                        context.Items.AddObject(dbItem);
                    }
                    context.SaveChanges();
                    result = true;
                }
            }
            catch (Exception ex)
            {
                result = false;
            }
            return result;
        }

        protected void CopyItem(Top.Api.Domain.Item item, Model.Item dbItem,bool detailInfo)
        {
            dbItem.AfterSaleId = item.AfterSaleId;
            dbItem.ApproveStatus = item.ApproveStatus;
            dbItem.AuctionPoint = item.AuctionPoint;
            dbItem.AutoFill = item.AutoFill;
            dbItem.Cid = item.Cid;
            dbItem.CodPostageId = item.CodPostageId;
            dbItem.Created = ConvertTo<DateTime>(item.Created);
            dbItem.DelistTime = ConvertTo<DateTime>(item.DelistTime);
            dbItem.Desc = item.Desc;
            dbItem.DetailUrl = item.DetailUrl;
            dbItem.EMSFee = ConvertTo<double>(item.EmsFee);
            dbItem.ExpressFee = ConvertTo<double>(item.ExpressFee);
            dbItem.FreightPlayer = item.FreightPayer;
            dbItem.HasDiscount = item.HasDiscount;
            dbItem.HasInvoice = item.HasInvoice;
            dbItem.HasShowcase = item.HasShowcase;
            dbItem.HasWarranty = item.HasWarranty;
            dbItem.Increment = item.Increment;
            dbItem.InputPids = item.InputPids;
            dbItem.InputStr = item.InputStr;
            dbItem.Is3D = item.Is3D;
            dbItem.IsEx = item.IsEx;
            dbItem.IsPrepay = item.IsPrepay;
            dbItem.IsTaobao = item.IsTaobao;
            dbItem.IsTiming = item.IsTiming;
            dbItem.IsVirtual = item.IsVirtual;
            dbItem.ListTime = ConvertTo<DateTime>(item.ListTime);
            dbItem.Modified = ConvertTo<DateTime>(item.Modified);
            dbItem.Nick = item.Nick;
            dbItem.Num = item.Num;
            dbItem.OneStation = item.OneStation;
            dbItem.OuterId = item.OuterId;
            dbItem.PicUrl = item.PicUrl;
            dbItem.PostageId = item.PostageId;
            dbItem.PostFee = ConvertTo<double>(item.PostFee);
            dbItem.Price = ConvertTo<double>(item.Price);
            dbItem.ProductId = item.ProductId;
            dbItem.PromotedService = item.PromotedService;
            dbItem.PropertyAlias = item.PropertyAlias;
            dbItem.Props = item.Props;
            dbItem.PropsName = item.PropsName;
            dbItem.SecondKill = item.SecondKill;
            dbItem.SellerCids = item.SellerCids;
            dbItem.SellPromise = item.SellPromise;
            dbItem.StuffStatus = item.StuffStatus;
            dbItem.Title = item.Title;
            dbItem.Type = item.Type;
            dbItem.ValidThru = dbItem.ValidThru;
            dbItem.Violation = item.Violation;
            dbItem.WapDesc = item.WapDesc;
            dbItem.WapDetailUrl = item.WapDetailUrl;
            dbItem.WWStatus = item.WwStatus;

            if (!detailInfo)
            {
                dbItem.Volume = item.Volume;
                dbItem.Score = item.Score;
            }
            if(item.Location!=null)
            {
                dbItem.LocationAddress = item.Location.Address;
                dbItem.LocationCity = item.Location.City;
                dbItem.LocationCountry = item.Location.Country;
                dbItem.LocationDistrict = item.Location.District;
                dbItem.LocationState = item.Location.State;
                dbItem.LocationZip = item.Location.Zip;
            }

            dbItem.IId = item.IId;
            dbItem.NumIId = item.NumIid;
        }

        protected string FormatOutput(string text)
        {
            return string.Format("{0} at {1}", text, DateTime.Now.ToString());
        }

        protected void OutputInfo(string text)
        {
            Console.WriteLine(FormatOutput(text));
        }

        protected int ProcessPageList<T>(string contentName, int pageSize, List<T> firstContentList, Func<int, List<T>> getNextContentList, Func<T, bool> processAction)            
        {
            return ProcessPageList<T>(contentName, pageSize, firstContentList, getNextContentList, processAction, firstContentList.Count);
        }

        protected int ProcessPageList<T>(string contentName, int pageSize, List<T> firstContentList, Func<int, List<T>> getNextContentList, Func<T, bool> processAction, long totalCount)
        {
            int success = 0;
            try
            {
                int pageCount = (int)(totalCount / pageSize) + ((totalCount % pageSize > 0) ? 1 : 0);
                OutputInfo("将要获取的" + contentName + "的总页数为:" + pageCount + "页，每页:" + pageSize + "条,共 " + totalCount.ToString() + " 条");
                int currentPageNo = 1;
                List<T> currentContentList = firstContentList;
                do
                {
                    try
                    {
                        OutputInfo("第" + currentPageNo + "页：");
                        foreach (T t in currentContentList)
                        {
                            if (processAction(t))
                                success++;
                        }
                    }
                    catch (Exception e)
                    {
                        OutputInfo(string.Format("更新第" + currentPageNo + "页的" + contentName + "失败:{0}", e.Message));
                        Thread.Sleep(1000);
                    }
                    currentPageNo++;
                    currentContentList = getNextContentList(currentPageNo);
                } while (currentPageNo <= pageCount);
            }
            catch { }
            return success;
        }

         
        protected Nullable<T> ConvertTo<T>(string text)
            where T:struct
        {
            try
            {
                TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
                if (converter != null)
                {
                    try
                    {
                        return (T)converter.ConvertFromString(text);
                    }
                    catch
                    {
                    }
                }
            }
            catch { }
            return null;
        }

        public int GetWeekOfYear(DateTime dateTime)
        {
            GregorianCalendar gregorianCalendar = new GregorianCalendar();
            return gregorianCalendar.GetWeekOfYear(dateTime, CalendarWeekRule.FirstDay, DayOfWeek.Monday);
        }

    }

    public class Query
    {
        public string Title { get; set; }
        public string Nicks { get; set; }
        public bool? WWStatus { get; set; }
        public string State { get; set; }
        public string City{get;set;}
        public string PromotedService{get;set;}
        public string StuffStatus { get; set; }
        public long? StartPrice { get; set; }
        public long? EndPrice { get; set; }
        public bool? AuctionFlag { get; set; }
        public bool? HasDiscount { get; set; }

    }
}
